#include "tileset.h"

#include <qfile.h>
#include <qdatastream.h>
#include <qstring.h>

#include "exceptions.h"

#include <qdebug.h>

using namespace std;
using namespace sf;

Tileset::Tileset(const QString& imagePath, const QString& propPath, const QString& name,
                 Uint8 tilesWidth, Uint8 tilesHeight)
    : m_spriteRef(imagePath), m_propFile(propPath), m_name(name),
      m_tilesWidth(tilesWidth), m_tilesHeight(tilesHeight), m_tiles((Tile***)0)
{
    m_width = m_spriteRef.GetSubRect().GetWidth() / m_tilesWidth;
    m_height = m_spriteRef.GetSubRect().GetHeight() / m_tilesHeight;

    //Creates the tiles matrix
    m_tiles = new Tile**[m_height];

    for(quint8 j = 0 ; j < m_height ; j++)
    {
        m_tiles[j] = new Tile*[m_width];
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            m_tiles[j][i] = new Tile(SpriteRef(imagePath,
                                               QPoint(0,0),
                                               QRect(i*m_tilesWidth, j*m_tilesHeight, m_tilesWidth, m_tilesHeight)));
        }
    }

    //If the file doesn't exist, fills the properties with zeros and saves them
    if(!m_propFile.exists())
    {
        fillProps((TileType)0);
        saveProps();
    }
    //If the file exists, reads the tile properties
    else
    {
        readProps();
    }
}

Tileset::~Tileset()
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            delete m_tiles[j][i];
        }
        delete[] m_tiles[j];
    }
    delete[] m_tiles;
}

void Tileset::saveProps()
{
    m_propFile.open(QIODevice::WriteOnly | QIODevice::Truncate);

    QDataStream outData(&m_propFile);

    //Writes the tileset name
    std::string name = m_name.toStdString();
    outData << name.c_str();

    //Writes the tiles properties
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            outData << (quint8)m_tiles[j][i]->getType();
        }
    }

    m_propFile.close();
}

void Tileset::readProps()
{
    m_propFile.open(QIODevice::ReadOnly);
    QDataStream inData(&m_propFile);
    quint8 tileType;

    //Reads the tileset name
    char* name;
    inData >> name;
    m_name = name;
    delete[] name;

    //Reads the tiles properties
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            inData >> tileType;
            m_tiles[j][i]->setType((TileType)tileType);
        }
    }

    m_propFile.close();
}

void Tileset::modifyProps(quint8 x, quint8 y, TileType value)
{
    if(x >= m_width)
        throw EX_OUT_OF_RANGE(m_width, x);
    else if (y >= m_height)
        throw EX_OUT_OF_RANGE(m_height, y);
    else
        m_tiles[y][x]->setType(value);
}

void Tileset::fillProps(TileType value)
{
    for(quint8 j = 0 ; j < m_height ; j++)
    {
        for(quint8 i = 0 ; i < m_width ; i++)
        {
            m_tiles[j][i]->setType(value);
        }
    }
}

const QString& Tileset::getName() const
{
    return m_name;
}

const Tile& Tileset::getTile(quint8 x, quint8 y) const
{
    if(x >= m_width)
        throw EX_OUT_OF_RANGE(m_width, x);
    else if (y >= m_height)
        throw EX_OUT_OF_RANGE(m_height, y);
    else
        return *m_tiles[y][x];
}

const Tile& Tileset::getTile(quint16 n) const
{
    try{
        return getTile(n % m_width, n / m_width);
    }
    catch(exception& e){
        throw;
    }
}


quint8 Tileset::getWidth() const
{
    return m_width;
}

quint8 Tileset::getHeight() const
{
    return m_height;
}

quint16 Tileset::getNum(quint8 x, quint8 y) const
{
    return (y*m_width+x);
}

const SpriteRef& Tileset::getSpriteRef() const
{
    return m_spriteRef;
}
